package com.project.smarthome.common.widget.gridview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ImageView;

public class CoolDragAndDropGridView extends SpanVariableGridView implements View.OnTouchListener {

    private static final int ITEM_HOVER_DELAY = 400;

    private int mDragPointX;
    private int mDragPointY;
    private int mDragOffsetX;
    private int mDragOffsetY;
    private int mDragPosition = AdapterView.INVALID_POSITION;
    private int mDropPosition = AdapterView.INVALID_POSITION;
    private int mCurrentPosition = AdapterView.INVALID_POSITION;
    private Runnable mDelayedOnDragRunnable = null;
    ScrollingStrategy mScrollingStrategy = null;
    WindowManager mWindowManager = null;
    WindowManager.LayoutParams mWindowParams = null;
    private ImageView mDragImageView = null;
    private boolean mDragAndDropStarted = false;
    private DragAndDropListener mDragAndDropListener = null;
    private OnTrackTouchEventsListener mOnTrackTouchEventsListener = null;

    public static interface OnTrackTouchEventsListener {

        void trackTouchEvents(final MotionEvent motionEvent);

    }

    public static interface DragAndDropListener {

        void onDragItem(int from);

        void onDraggingItem(int from, int to);

        void onDropItem(int from, int to);

        boolean isDragAndDropEnabled(int position);
    }

    public CoolDragAndDropGridView(Context context) {
        super(context);

        initialize();
    }

    public CoolDragAndDropGridView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        initialize();
    }

    public CoolDragAndDropGridView(Context context, AttributeSet attrs) {
        super(context, attrs);

        initialize();
    }

    private void initialize() {
        setOnTouchListener(this);
        setChildrenDrawingOrderEnabled(true);
    }

    public void startDragAndDrop() {
        mDragAndDropStarted = true;

    }

    public void stopDragAndDrop() {

        mDragAndDropStarted = false;

    }

    public void setDragAndDropListener(DragAndDropListener dragAndDropListener) {

        mDragAndDropListener = dragAndDropListener;

    }

    public void destroyDragImageView() {

        if (mDragImageView != null) {

            mWindowManager.removeView(mDragImageView);

            BitmapDrawable bitmapDrawable = (BitmapDrawable) mDragImageView.getDrawable();
            if (bitmapDrawable != null) {
                final Bitmap bitmap = bitmapDrawable.getBitmap();
                if (bitmap != null && !bitmap.isRecycled()) {
                    bitmap.recycle();
                }
            }

            mDragImageView.setImageDrawable(null);
            mDragImageView = null;
        }

    }

    private ImageView createDragImageView(final View v, final int x, final int y) {

        v.destroyDrawingCache();
        v.setDrawingCacheEnabled(true);
        Bitmap bm = Bitmap.createBitmap(v.getDrawingCache());

        mDragPointX = x - v.getLeft();
        mDragPointY = y - v.getTop();

        mWindowParams = new WindowManager.LayoutParams();
        mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;

        mWindowParams.x = x - mDragPointX + mDragOffsetX;
        mWindowParams.y = y - mDragPointY + mDragOffsetY;

        mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;

        mWindowParams.format = PixelFormat.TRANSLUCENT;
        mWindowParams.alpha = 0.6f;
        mWindowParams.windowAnimations = 1;

        ImageView iv = new ImageView(getContext());
        iv.setBackgroundColor(Color.parseColor("#e6e6e6"));
        iv.setImageBitmap(bm);
        mWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);// "window"
        mWindowManager.addView(iv, mWindowParams);

        return iv;

    }

    private void startDrag(final int x, final int y) {

        final View v = getChildAt(mDragPosition);
        v.clearAnimation();
        destroyDragImageView();
        mDragImageView = createDragImageView(v, x, y);
        v.setVisibility(INVISIBLE);
        if (mDragAndDropListener != null) {
            mDragAndDropListener.onDragItem(mDragPosition);
        }
    }

    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        if (mCurrentPosition == -1)
            return i;
        else if (i == childCount - 1)
            return mCurrentPosition;
        else if (i >= mCurrentPosition)
            return i + 1;
        return i;
    }

    private void onDrop() {
        mDragImageView.setVisibility(GONE);
        View v = getChildAt(mDropPosition);
        v.clearAnimation();
        destroyDragImageView();
        removeCallbacks(mDelayedOnDragRunnable);
        v.setVisibility(VISIBLE);
        if (mDragAndDropListener != null && mDropPosition != AdapterView.INVALID_POSITION) {
            mDragAndDropListener.onDropItem(mDragPosition, mDropPosition);
        }

        mDragPosition = mDropPosition = mCurrentPosition = AdapterView.INVALID_POSITION;
        mDragAndDropStarted = false;
    }

    public void setScrollingStrategy(ScrollingStrategy scrollingStrategy) {

        mScrollingStrategy = scrollingStrategy;

    }

    private void onDrag(final int x, final int y) {

        if (mScrollingStrategy != null && mScrollingStrategy.performScrolling(x, y, this)) {

            removeCallbacks(mDelayedOnDragRunnable);

            return;
        }

        final int tempDropPosition = pointToPosition(mCurrentPosition, x, y);

        if (mDragAndDropListener != null && mDropPosition != tempDropPosition && tempDropPosition != AdapterView.INVALID_POSITION) {

            removeCallbacks(mDelayedOnDragRunnable);

            if (mDragAndDropListener.isDragAndDropEnabled(tempDropPosition)) {

                mDropPosition = tempDropPosition;

                mDelayedOnDragRunnable = new Runnable() {

                    @Override
                    public void run() {
                        mDragAndDropListener.onDraggingItem(mCurrentPosition, tempDropPosition);
                        performDragAndDropSwapping(mCurrentPosition, tempDropPosition);

                        final int nextDropPosition = pointToPosition(tempDropPosition, x, y);

                        if (nextDropPosition == AdapterView.INVALID_POSITION) {

                            mCurrentPosition = mDropPosition = tempDropPosition;

                        }
                    }
                };

                postDelayed(mDelayedOnDragRunnable, ITEM_HOVER_DELAY);

            } else {

                mDropPosition = mDragPosition;

            }

        }

        if (mDragImageView != null) {

            mWindowParams.x = x - mDragPointX + mDragOffsetX;
            mWindowParams.y = y - mDragPointY + mDragOffsetY;
            mWindowManager.updateViewLayout(mDragImageView, mWindowParams);
        }

    }

    public void setOnTrackTouchEventListener(OnTrackTouchEventsListener onTrackTouchEventsListener) {

        mOnTrackTouchEventsListener = onTrackTouchEventsListener;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {

        if (mOnTrackTouchEventsListener != null) {
            mOnTrackTouchEventsListener.trackTouchEvents(event);
        }

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:

                if (mDragAndDropListener != null && mDragAndDropStarted) {

                    mDragAndDropStarted = false;

                    getParent().requestDisallowInterceptTouchEvent(true);

                    return launchDragAndDrop(event);
                }

                break;

            default:
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:

                mDragAndDropStarted = false;

                getParent().requestDisallowInterceptTouchEvent(false);

                break;
        }

        return super.onInterceptTouchEvent(event);
    }

    private boolean launchDragAndDrop(final MotionEvent event) {

        final int x = (int) event.getX();
        final int y = (int) event.getY();

        mCurrentPosition = mDragPosition = mDropPosition = pointToPosition(mDragPosition, x, y);

        if (mDragPosition != AdapterView.INVALID_POSITION && mDragAndDropListener.isDragAndDropEnabled(mDragPosition)) {

            mDragOffsetX = (int) (event.getRawX() - x);
            mDragOffsetY = (int) (event.getRawY() - y);

            startDrag(x, y);

            return true;
        }

        return false;
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {

        if (mDragPosition != AdapterView.INVALID_POSITION && mDragImageView != null) {

            final int x = (int) event.getX();
            final int y = (int) event.getY();
            switch (event.getAction()) {
                case MotionEvent.ACTION_MOVE:
                    mDragOffsetX = (int) (event.getRawX() - x);
                    mDragOffsetY = (int) (event.getRawY() - y);
                    onDrag(x, y);
                    break;

                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    onDrop();
                    resetLongClickTransition();
                    getParent().requestDisallowInterceptTouchEvent(false);
                    return false;
                default:
            }
            return true;
        }
        return false;
    }
}